﻿/*
 * ©2009-2010 XELF
 * http://xelf.info
 */

using System;

namespace XELF.Framework {

	/// <summary>
	/// 数学補助
	/// </summary>
	public static partial class MathHelper {
		/// <summary>
		/// 要素入れ替え
		/// </summary>
		public static void Swap<T>(ref T a, ref T b) {
			var c = a;
			a = b;
			b = c;
		}
		/// <summary>
		/// 配列の長さを変更します。配列がnullの場合には、配列を確保します。
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="array"></param>
		/// <param name="length"></param>
		public static void Resize<T>(ref T[] array, int length) {
			if (array == null) {
				array = new T[length];
			} else if (array.Length < length) {
				array = new T[length];
			}
		}

		/// <summary>
		/// 逆数
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Rcp(float x) {
			return unchecked(1.0f / x);
		}
#if !XNA
		/// <summary>
		/// クランプする。
		/// </summary>
		/// <param name="value">元の値</param>
		/// <param name="min">最小</param>
		/// <param name="max">最大</param>
		/// <returns></returns>
		public static float Clamp(float value, float min, float max) {
			return Math.Min(Math.Max(value, min), max);
		}

		/// <summary>
		/// クランプする。
		/// </summary>
		/// <param name="value">元の値</param>
		/// <param name="min">最小</param>
		/// <param name="max">最大</param>
		/// <returns></returns>
		public static int Clamp(int value, int min, int max) {
			return Math.Min(Math.Max(value, min), max);
		}
#endif
		/// <summary>
		/// 円周率
		/// </summary>
		public const float Pi = (float)Math.PI;

		/// <summary>
		/// 円周率の2倍
		/// </summary>
		public const float TwoPi = (float)(2 * Math.PI);

		/// <summary>
		/// 円周率の1/2倍
		/// </summary>
		public const float PiOver2 = (float)(Math.PI / 2);

		/// <summary>
		/// 円周率の1/4倍
		/// </summary>
		public const float PiOver4 = (float)(Math.PI / 4);

		/// <summary>
		/// ネイピア数（自然対数の底）
		/// </summary>
		public const float E = (float)Math.E;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="degrees"></param>
		/// <returns></returns>
		public static float ToRadians(float degrees) {
			return degrees * (180 / Pi);
		}
	}

	public static partial class MathHelper {
		/// <summary>
		/// 三角形の点（特定の軸での座標）と重心座標から座標を計算する。
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="c"></param>
		/// <param name="amountB"></param>
		/// <param name="amountC"></param>
		/// <returns></returns>
		public static float Barycentric(float a, float b, float c, float amountB, float amountC) {
			return (1 - amountB - amountC) * a + amountB * b + amountC * c;
		}

		/// <summary>
		/// 線形補間。
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="amount"></param>
		public static float Lerp(float a, float b, float amount) {
			return a + (b - a) * amount;
		}

		/// <summary>
		/// 双線形補間。
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="c"></param>
		/// <param name="d"></param>
		/// <param name="amountU"></param>
		/// <param name="amountV"></param>
		public static float Bilinear(float a, float b, float c, float d,
			float amountU, float amountV) {
			return Lerp(Lerp(a, b, amountU), Lerp(c, d, amountU), amountV);
		}
	}

	public static partial class MathHelper {

		/// <summary>
		/// 
		/// </summary>
		/// <param name="random"></param>
		/// <returns></returns>
		public static float NextSingle(this Random random) {
			return (float)random.NextDouble();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Sin(float r) {
			return (float)Math.Sin(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Sinh(float r) {
			return (float)Math.Sinh(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Sinc(float r) {
			if (Math.Abs(r) < 1e-4f)
				return 1.0f - r * r * (1.0f / 6);
			return (float)Math.Sin(r) / r;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Lanczos2(float x) {
			var y = x * MathHelper.Pi;
			if (x <= -2.0f || x >= 2.0f) return 0.0f;
			return 2.0f * Sinc(y) * Sinc(y * 0.5f);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Cos(float r) {
			return (float)Math.Cos(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Cosh(float r) {
			return (float)Math.Cosh(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Tan(float r) {
			return (float)Math.Tan(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Cot(float r) {
			return MathHelper.Rcp((float)Math.Tan(r));
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Tanh(float r) {
			return (float)Math.Tanh(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Asin(float r) {
			return (float)Math.Asin(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Acos(float r) {
			return (float)Math.Acos(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Atan(float r) {
			return (float)Math.Atan(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="y"></param>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Atan2(float y, float x) {
			return (float)Math.Atan2(y, x);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Floor(float r) {
			return (float)Math.Floor(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Ceiling(float r) {
			return (float)Math.Ceiling(r);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Round(float r) {
			return (float)Math.Round(r);
		}
#if! (XBOX || SILVERLIGHT || WINDOWS_PHONE)
		/// <summary>
		/// 
		/// </summary>
		/// <param name="r"></param>
		/// <returns></returns>
		public static float Truncate(float r) {
			return (float)Math.Truncate(r);
		}
#endif
		const float log2 = 0.693147181f; // == Log(2);
		const float inverseLog2 = 1.44269504f; // == 1 / Log(2);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Exp(float x) {
			return (float)Math.Exp(x);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Exp2(float x) {
			return Exp(log2 * x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Log(float x) {
			return (float)Math.Log(x);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Log10(float x) {
			return (float)Math.Log10(x);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Log2(float x) {
			return Log(x) * inverseLog2;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public static float Pow(float x, float y) {
			return (float)Math.Pow(x, y);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static float Sqrt(float x) {
			return (float)Math.Sqrt(x);
		}

		/// <summary>
		/// 単位階段関数
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static int UnitStep(float x) {
			Union32 u = new Union32 { Single = x, };
			return (int)~(u.Int32 >> 31) & 1;
			//return (Math.Sign(x) + 2) >> 1;
		}
		/// <summary>
		/// 単位階段関数
		/// </summary>
		/// <param name="x"></param>
		/// <returns></returns>
		public static int UnitStep(int x) {
			return (Math.Sign(x) + 2) >> 1;
		}

		/// <summary>
		/// 三角形の面積（3辺の長さだけがわかっている場合）
		/// </summary>
		/// <param name="lengthA"></param>
		/// <param name="lengthB"></param>
		/// <param name="lengthC"></param>
		/// <returns></returns>
		public static float TriangleArea(float lengthA, float lengthB, float lengthC) {
			var s = (lengthA + lengthB + lengthC) * 0.5f;
			return MathHelper.Sqrt(s * (s - lengthA) * (s - lengthB) * (s - lengthC));
		}

		public static bool AreEqual(float a, float b, float delta) {
			return Math.Abs(a - b) <= delta;
		}

		public static bool AreEqual(double a, double b, double delta) {
			return Math.Abs(a - b) <= delta;
		}
	}

}
